import matches from 'lodash/matches'

import { BatchEvent } from '@slardar/sdk-template'

export const findAndAssert = findMultiAndAssert.bind(null, 1)

export function findMultiAndAssert(count: number, findFn: (ev: any) => boolean, assertFn: (ev: any) => void) {
  let data = [] as any[]
  function assert(body: BatchEvent<any>) {
    data = data.concat(body?.list?.filter(findFn) || [])
    if (data.length < count) {
      cy.wait('@post', { timeout: 2000 }).its('request.body').then(assert)
    } else {
      data.forEach(assertFn)
    }
  }
  cy.wait('@post').its('request.body').then(assert)
}

export const findAndAssertAll = findMultiAndAssertAll.bind(null, 1)

export function findMultiAndAssertAll(count: number, findFn: (ev: any) => boolean, assertFn: (data: any[]) => void) {
  let data = [] as any[]
  let allData = [] as any[]
  function assert(body: BatchEvent<any>) {
    allData = allData.concat(body.list)
    data = data.concat(body?.list?.filter(findFn) || [])
    if (data.length < count) {
      cy.wait('@post', { timeout: 2000 }).its('request.body').then(assert)
    } else {
      assertFn(allData)
    }
  }
  cy.wait('@post').its('request.body').then(assert)
}
export const matchAndAssertAll = matchMultiAndAssertAll.bind(null, 1)

export const TestCustomEvent = {
  ev_type: 'custom',
  payload: {
    name: 'test-custom-event',
    metrics: {
      a: 1,
    },
    categories: {
      b: '1',
    },
    type: 'event',
  },
}
export function matchMultiAndAssertAll(count: number, findObj: any, assertFn: (data: any[]) => void) {
  findMultiAndAssertAll(count, matches(findObj), assertFn)
}

export const matchAndAssert = matchMultiAndAssert.bind(null, 1)
export function matchMultiAndAssert(count: number, findObj: any, assertFn: (ev: any) => void) {
  findMultiAndAssert(count, matches(findObj), assertFn)
}

export function findMultiAndMatch(
  count: number,
  findFn: (ev: any) => boolean,
  matchFn: (ev: any) => boolean,
  timeout = 2000,
) {
  let data = [] as any[]
  function assert(body: BatchEvent<any>) {
    data = data.concat(body?.list?.filter(findFn).filter(matchFn) || [])
    if (data.length < count) {
      cy.wait('@post', { timeout }).its('request.body').then(assert)
    } else {
      expect(true).to.be.equal(true)
    }
  }
  cy.wait('@post').its('request.body').then(assert)
}

export const matchAndMatch = matchMultiAndMatch.bind(null, 1)

export function matchMultiAndMatch(count: number, findObj: any, matchObj: any, timeout?: number) {
  findMultiAndMatch(count, matches(findObj), matches(matchObj), timeout)
}

export function checkIsExistWith(findObj: any) {
  let data = [] as any[]
  function assert(body: BatchEvent<any>) {
    data = data.concat(body?.list?.filter(matches(findObj)) || [])
    expect(data.length).to.be.equal(0)
  }
  cy.wait('@post').its('request.body').then(assert)
}

export interface SlardarRegistryType {
  __SLARDAR_REGISTRY__: {
    Slardar: {
      plugins: { name: string; version: string; apply: () => void }[]
      errors: any[]
      subject: any
    }
  }
}

export function waitUrlWithCallback(url: string, cb: () => void) {
  const reg = new RegExp(url)
  cy.intercept('GET', reg).as('common')
  cy.wait('@common')
    .its('request.url')
    .then(() => {
      cb()
    })
}

// 当断言的事件较多时，推荐提前设置 batchSender 容量为 infinity
export function waitBatchReqAndAssert(assertFn: (body: BatchEvent<any>) => boolean, timeout = 2000, maxTryCount = 1) {
  let tryCount = 0
  function _wait() {
    tryCount += 1
    cy.wait('@post', { timeout })
      .its('request.body')
      .then((body: BatchEvent<any>) => {
        if (!body.list || !assertFn(body)) {
          if (tryCount < maxTryCount) {
            _wait()
          } else {
            expect(false).true
          }
        } else {
          expect(true).true
        }
      })
  }
  _wait()
}

// 发送的事件完全匹配 shouldMatchObjs 但全都不匹配 shouldNotMatchObjs, 推荐提前设置 batchSender 容量为 infinity ，关闭其他干扰插件
export function matchAndNotMatch(
  body: BatchEvent<any>,
  shouldMatchObjs: any[],
  shouldNotMatchObjs?: any[],
  matchedCount?: number,
): boolean {
  const shouldMatchAll = shouldMatchObjs.every((obj) => {
    const m = body.list.filter(matches(obj))
    if (typeof matchedCount === 'number' && matchedCount >= 0) {
      return m.length === matchedCount
    }
    return m.length >= 1
  })
  const shouldNotMatchAll = shouldNotMatchObjs
    ? shouldNotMatchObjs.every((obj) => body.list.filter(matches(obj)).length === 0)
    : true
  return shouldMatchAll && shouldNotMatchAll
}
